package xyz.ibenben.zhongdian.system.controller;

import net.sf.json.JSONObject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import xyz.ibenben.zhongdian.common.annotation.SystemControllerLog;
import xyz.ibenben.zhongdian.common.configure.AmqpConfig;
import xyz.ibenben.zhongdian.common.constants.Constants;
import xyz.ibenben.zhongdian.common.exception.ExceptionEnum;
import xyz.ibenben.zhongdian.common.exception.MyException;
import xyz.ibenben.zhongdian.common.shiro.PasswordHelper;
import xyz.ibenben.zhongdian.common.util.HttpServletResponseUtil;
import xyz.ibenben.zhongdian.common.util.JsonConvertUtil;
import xyz.ibenben.zhongdian.common.util.RandomUtil;
import xyz.ibenben.zhongdian.system.entity.ChatRecord;
import xyz.ibenben.zhongdian.system.entity.ajax.AjaxJson;
import xyz.ibenben.zhongdian.system.entity.enums.ChatTypeEnum;
import xyz.ibenben.zhongdian.system.entity.sys.SysResources;
import xyz.ibenben.zhongdian.system.entity.sys.SysRole;
import xyz.ibenben.zhongdian.system.entity.sys.SysUser;
import xyz.ibenben.zhongdian.system.entity.sys.SysUserRole;
import xyz.ibenben.zhongdian.system.service.ChatRecordService;
import xyz.ibenben.zhongdian.system.service.impl.EmailSenderService;
import xyz.ibenben.zhongdian.system.service.sys.SysResourcesService;
import xyz.ibenben.zhongdian.system.service.sys.SysRoleService;
import xyz.ibenben.zhongdian.system.service.sys.SysUserRoleService;
import xyz.ibenben.zhongdian.system.service.sys.SysUserService;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * 登录注册控制类
 * 提供了一些基本的服务，如登录、注册、修改密码等方法。
 * spring mvc思想 通过controller来调用service里的方法，
 * service里的方法再通过调用Dao来实现对数据的操作
 * 返回值统统是String类型的返回页面有统一的安排方便代码提取
 *
 * @author chenjian
 * @since 2017年9月27日上午10:57:28
 */
@Controller
@RequestMapping(value = "/")
public class LoginController extends BaseController {
    /**
     * 登录页面
     */
    private static final String LOGIN = "login/login";
    /**
     * 跳转首页
     */
    private static final String INDEX = "redirect:/index";
    /**
     * 确认邮件页面
     */
    private static final String EMAILPAGE = "login/emailActivateFailure";
    /**
     * 忘记密码
     */
    private static final String FORGETCODE = "forgetPwdCode";
    /**
     * 确认码时间
     */
    private static final String VERIFIED = "verificationCodeTime";
    /**
     * 修改密码页面
     */
    private static final String MODIFYPAGE = "login/modifyPassword";

    @Resource
    private SysResourcesService resourcesService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysRoleService sysRoleService;
    @Resource
    private SysUserRoleService userRoleService;
    @Resource
    private EmailSenderService emailSenderService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private ChatRecordService chatRecordService;

    @Value("${spring.rabbitmq.host}")
    private String host;

    /**
     * 打开登陆页面
     *
     * @return 页面
     */
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    @SystemControllerLog(description = "打开登陆页面")
    public String login() {
        return LOGIN;
    }

    /**
     * 登陆
     *
     * @param user 参数
     * @param map  参数
     * @return 页面
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @SystemControllerLog(description = "登陆")
    public String login(SysUser user, Model map, HttpServletRequest request) {
        //用户名或密码不能为空！
        if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) {
            return HttpServletResponseUtil.processModelAndView(map, "用户名或密码不能为空！", LOGIN);
        }
        //账户已锁定！
        SysUser recordUser = sysUserService.selectByUsername(user.getUsername());
        if (recordUser != null && recordUser.getEnable() == 0) {
            return HttpServletResponseUtil.processModelAndView(map, "账户已锁定！", LOGIN);
        }

        UsernamePasswordToken token = processToken(user);
        return gotoPage(token, request, true);
    }

    /**
     * 处理token
     *
     * @param user 用户
     * @return token
     */
    private UsernamePasswordToken processToken(SysUser user) {
        if (StringUtils.isEmpty(user.getRememberMe())) {
            return new UsernamePasswordToken(user.getUsername(), user.getPassword());
        } else {
            return new UsernamePasswordToken(user.getUsername(), user.getPassword(), "on".equals(user.getRememberMe()));
        }
    }

    /**
     * 打开首页
     *
     * @param map     参数
     * @param request 请求
     * @return 页面
     */
    @RequestMapping(value = {"/index", ""}, method = RequestMethod.GET)
    @SystemControllerLog(description = "打开首页")
    public String index(Model map, HttpServletRequest request) {
        request.setAttribute("host", host);

        //发送登录弹窗
        SysUser user = (SysUser) request.getSession().getAttribute(Constants.SESSION);
        CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
        JSONObject obj = new JSONObject();
        obj.put("type", 1);
        obj.put("username", user.getUsername());
        rabbitTemplate.convertAndSend(AmqpConfig.EXCHANGE, AmqpConfig.ROUTINGKEY, obj.toString(), correlationId);

        //获取聊天记录
        List<ChatRecord> chatList = chatRecordService.selectByLimit(0, 10, null, null, 1);
        Collections.reverse(chatList);
        map.addAttribute("chatList", chatList);

        //获取所有用户
        List<SysUser> list = sysUserService.selectAll();
        map.addAttribute("userList", list);
        return "index";
    }

    /**
     * 发送聊天记录
     *
     * @param toId     参数
     * @param type     参数
     * @param content  实体
     * @param request  请求
     * @param response 参数
     * @return 页面
     */
    @RequestMapping(value = "/sendMessage", method = RequestMethod.POST)
    @SystemControllerLog(description = "发送聊天记录")
    @ResponseBody
    public String sendMessage(Long toId, Integer type, String content, HttpServletRequest request, HttpServletResponse response) {
        //没有输入文字
        if (null == content || StringUtils.isEmpty(content)) {
            return HttpServletResponseUtil.processErrorStatus(response, "你倒是输入点东西啊");
        } else {
            //组装聊天实体
            ChatRecord record = new ChatRecord();
            record.setToId(toId);
            record.setType(ChatTypeEnum.getByValue(type));
            record.setContent(content);
            SysUser user = (SysUser) request.getSession().getAttribute(Constants.SESSION);
            record.setFromId(user.getId());
            record.setFromUserName(user.getUsername());
            record.setHead(user.getHead());
            record.setFontColor(user.getFontColor());
            record.setBackColor(user.getBackColor());
            if (record.getToId() != null) {
                record.setToUserName(sysUserService.selectByKey(record.getToId()).getUsername());
            }
            record.setDelFlag(0);
            record.setSendTime(new Date());
            //保存聊天记录
            chatRecordService.save(record, request, user.getId());
            CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
            JSONObject obj = new JSONObject();
            if (record.getType().ordinal() == 1) {
                obj.put("type", 2);
            } else {
                obj.put("type", 3);
            }
            JsonConvertUtil.jsonDateFormatObject(obj, "record", record);
            //发送MQ
            rabbitTemplate.convertAndSend(AmqpConfig.EXCHANGE, AmqpConfig.ROUTINGKEY, obj.toString(), correlationId);
            return HttpServletResponseUtil.processSuccessStatus(response, "");
        }
    }

    /**
     * 获取聊天记录
     *
     * @param fromId   参数
     * @param toId     参数
     * @param type     参数
     * @param response 参数
     * @return 页面
     */
    @RequestMapping(value = "/getMessage", method = RequestMethod.POST)
    @SystemControllerLog(description = "获取聊天记录")
    @ResponseBody
    public String getMessage(Long fromId, Long toId, Integer type, HttpServletResponse response) {
        AjaxJson aj = new AjaxJson();
        //获取聊天记录
        List<ChatRecord> list = chatRecordService.selectByLimit(0, 10, fromId, toId, type);
        if (list != null && !list.isEmpty()) {
            //顺序倒置一下
            Collections.reverse(list);
            JSONObject obj = new JSONObject();
            JsonConvertUtil.jsonDateFormatArray(obj, "messages", list);
            aj.setObj(obj);
            HttpServletResponseUtil.processSuccessStatus(response, aj);
            return null;
        } else {
            return HttpServletResponseUtil.processErrorStatus(response, "没有找到聊天记录");
        }
    }

    /**
     * 获取查找信息
     *
     * @param search 参数
     * @return 页面
     */
    @RequestMapping(value = "/search")
    @SystemControllerLog(description = "获取查找信息")
    public String search(String search) {
        if (search.contains("添加") || search.contains("列表")) {
            List<SysResources> list = resourcesService.findByType(2);
            for (SysResources sr : list) {
                if (search.equals(sr.getName())) {
                    return "redirect:" + sr.getResUrl();
                }
            }
        }
        return "index";
    }

    /**
     * 注册用户信息
     *
     * @param user     参数
     * @param request  请求
     * @param response 参数
     * @return 页面
     */
    @RequestMapping(value = "/regist", method = RequestMethod.POST)
    @SystemControllerLog(description = "注册用户信息")
    @ResponseBody
    public String regist(SysUser user, HttpServletRequest request, HttpServletResponse response) {
        if (null == user || StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())
                || StringUtils.isEmpty(user.getEmail())) {
            return HttpServletResponseUtil.processErrorStatus(response, "用户信息不完整，请检查");
        }
        String result = processReg(user, response);
        if (Constants.OK.equals(result)) {
            return processRegist(user, request, response);
        }
        return result;
    }

    private String processReg(SysUser user, HttpServletResponse response) {
        if (!user.getPassword().equals(user.getCpassword())) {
            return HttpServletResponseUtil.processErrorStatus(response, "两次密码不一致，请检查");
        }
        if (sysUserService.checkExist(user.getEmail(), null) != null) {
            return HttpServletResponseUtil.processErrorStatus(response, "邮箱已经存在，请更换");
        }
        if (sysUserService.checkExist(null, user.getUsername()) != null) {
            return HttpServletResponseUtil.processErrorStatus(response, "用户名已经存在，请更换");
        }
        return Constants.OK;
    }

    /**
     * 处理注册信息
     *
     * @param user     用户
     * @param request  请求
     * @param response 响应
     * @return 页面
     */
    private String processRegist(SysUser user, HttpServletRequest request, HttpServletResponse response) {
        user.setType(false);
        user.setEnable(0);
        user.setFontColor(RandomUtil.getRandomColor());
        user.setBackColor(RandomUtil.getRandomColor());
        sysUserService.save(user, request);
        //发送激活邮件
        String url = "http://" + request.getServerName() + ":" + request.getServerPort();
        String image = url + "/images/a.jpg";
        emailSenderService.sendRegistEmail(user.getId(), user.getUsername(), user.getEmail(), url, image);
        return HttpServletResponseUtil.processSuccessStatus(response, "");
    }

    /**
     * 邮箱激活
     *
     * @param userId  用户主键
     * @param email   邮箱
     * @param request 请求
     * @param map     参数
     * @return 页面
     */
    @RequestMapping(value = "/emailActivate", method = RequestMethod.GET)
    @SystemControllerLog(description = "激活邮箱")
    public String emailActivate(Long userId, String email, HttpServletRequest request, Model map) {
        SysUser user = sysUserService.selectByKey(userId);
        if (user == null) {
            return HttpServletResponseUtil.processModelAndView(map, "用户不存在！", EMAILPAGE);
        }
        if (!user.getEmail().equals(email)) {
            return HttpServletResponseUtil.processModelAndView(map, "用户邮箱不匹配！", EMAILPAGE);
        }
        if (user.getEnable() == 1) {
            return HttpServletResponseUtil.processModelAndView(map, "用户已激活账号，不能重复激活！", EMAILPAGE);
        }
        //处理邮件激活
        processEmailActivate(user, request);
        UsernamePasswordToken token = processToken(user);
        return gotoPage(token, request, false);
    }

    /**
     * 跳转页面
     *
     * @param token       token
     * @param request     请求
     * @param isLoginPage 是否是登录页面
     * @return 页面
     */
    private String gotoPage(UsernamePasswordToken token, HttpServletRequest request, boolean isLoginPage) {
        try {
            return processGoToPage(token, request.getSession());
        } catch (LockedAccountException lae) {
            processLockedAccountException(isLoginPage, lae);
        } catch (ExcessiveAttemptsException eae) {
            processExcessiveAttemptsException(isLoginPage, eae);
        } catch (AuthenticationException ae) {
            processAuthenticationException(isLoginPage, ae);
        } finally {
            token.clear();
        }
        return null;
    }

    private void processLockedAccountException(boolean isLoginPage, LockedAccountException lae) {
        throw new MyException(isLoginPage ? ExceptionEnum.LUCKUSEREXCEPTION2 : ExceptionEnum.LUCKUSEREXCEPTION, lae);
    }

    private void processExcessiveAttemptsException(boolean isLoginPage, ExcessiveAttemptsException eae) {
        throw new MyException(isLoginPage ? ExceptionEnum.PASSWORDERROREXCEPTION2 : ExceptionEnum.PASSWORDERROREXCEPTION, eae);
    }

    private void processAuthenticationException(boolean isLoginPage, AuthenticationException ae) {
        throw new MyException(isLoginPage ? ExceptionEnum.USERORPASSWORDERROREXCEPTION2 : ExceptionEnum.USERORPASSWORDERROREXCEPTION, ae);
    }

    /**
     * 处理跳转页面
     *
     * @param token token
     * @return 页面
     */
    private String processGoToPage(UsernamePasswordToken token, HttpSession session) {
        Subject subject = SecurityUtils.getSubject();
        subject.login(token);
        // 获取保存的URL
        Object obj = token.getPrincipal();
        SysUser user = sysUserService.selectUserWithRoleByUsername(obj.toString());
        session.setAttribute(Constants.SESSIONID, user.getId());
        session.setAttribute(Constants.SESSION, user);
        return INDEX;
    }

    /**
     * 处理邮件激活
     *
     * @param user    用户
     * @param request 请求
     */
    private void processEmailActivate(SysUser user, HttpServletRequest request) {
        //用户开启使用
        user.setEnable(1);
        sysUserService.updateAll(user, request);
        SysUserRole sur = new SysUserRole();
        sur.setUserId(user.getId());
        SysRole role = sysRoleService.selectByRoleDesc(null, "普通用户");
        sur.setRoleId(role.getId());
        //添加角色为用户
        userRoleService.addUserRole(sur);
    }

    /**
     * 获取验证码
     *
     * @param email    邮箱
     * @param request  请求
     * @param response 响应
     * @return 页面
     */
    @RequestMapping(value = "/getVerifyCode", method = RequestMethod.POST)
    @SystemControllerLog(description = "获取验证码")
    @ResponseBody
    public String getVerifyCode(String email, HttpServletRequest request, HttpServletResponse response) {
        if (StringUtils.isEmpty(email)) {
            return HttpServletResponseUtil.processErrorStatus(response, "您输入的邮箱不能为空！");
        }
        SysUser user = sysUserService.checkExist(email, null);
        if (user == null) {
            return HttpServletResponseUtil.processErrorStatus(response, "您输入的邮箱不存在！");
        }
        //发送邮箱确认找回密码
        String url = "http://" + request.getServerName() + ":" + request.getServerPort();
        String image = url + "/images/a.jpg";
        Map<String, Object> resultMap = emailSenderService.sendResetPasswordEmail(user.getUsername(), user.getEmail(), url, image);
        request.getSession().setAttribute(FORGETCODE, resultMap.get(FORGETCODE));
        request.getSession().setAttribute(VERIFIED, resultMap.get(VERIFIED));
        return HttpServletResponseUtil.processSuccessStatus(response, "180");
    }

    /**
     * 找回密码
     *
     * @param email         邮箱
     * @param forgetPwdCode 忘记密码代码
     * @param request       请求
     * @param response      响应
     * @return 页面
     */
    @RequestMapping(value = "/pwdBack", method = RequestMethod.POST)
    @SystemControllerLog(description = "找回密码")
    @ResponseBody
    public String pwdBack(String email, String forgetPwdCode, HttpServletRequest request, HttpServletResponse response) {
        if (StringUtils.isEmpty(forgetPwdCode)) {
            return HttpServletResponseUtil.processErrorStatus(response, "您输入的验证码不能为空！");
        }
        String forgetPwdCodeInSession = (String) request.getSession().getAttribute(FORGETCODE);
        Long verificationCodeTimeInSession = (Long) request.getSession().getAttribute(VERIFIED);
        SysUser user = sysUserService.checkExist(email, null);
        if (user == null) {
            return HttpServletResponseUtil.processErrorStatus(response, "您输入的邮箱不存在！");
        }
        if (System.currentTimeMillis() / 1000 >= verificationCodeTimeInSession) {
            return HttpServletResponseUtil.processErrorStatus(response, "您输入的验证码已超时！");
        }
        if (!forgetPwdCode.equals(forgetPwdCodeInSession)) {
            return HttpServletResponseUtil.processErrorStatus(response, "您输入的验证码不正确！");
        }
        return processPwdBack(user, request, response);
    }

    /**
     * 处理密码找回
     *
     * @param user     用户
     * @param request  请求
     * @param response 响应
     * @return 页面
     */
    private String processPwdBack(SysUser user, HttpServletRequest request, HttpServletResponse response) {
        user.setPassword("123456");
        PasswordHelper p = new PasswordHelper();
        p.encryptPassword(user);
        sysUserService.updateAll(user, request);
        return HttpServletResponseUtil.processSuccessStatus(response, "您的账号密码已经更改为‘123456’，请尽快登录系统后修改密码！");
    }

    /**
     * 打开修改密码页面
     *
     * @param map     参数
     * @param request 请求
     * @return 页面
     */
    @RequestMapping(value = "/modifyPassword", method = RequestMethod.GET)
    @SystemControllerLog(description = "打开修改密码页面")
    public String modifyPassword(Model map, HttpServletRequest request) {
        map.addAttribute("userId", request.getSession().getAttribute(Constants.SESSIONID).toString());
        return MODIFYPAGE;
    }

    /**
     * 提交修改密码页面
     *
     * @param map     参数
     * @param user    用户
     * @param request 请求
     * @return 页面
     */
    @RequestMapping(value = "/modifyPassword", method = RequestMethod.POST)
    @SystemControllerLog(description = "更新新密码")
    public String modifyPassword(Model map, SysUser user, HttpServletRequest request) {
        String result = processModifyPwd(user, map);
        if (Constants.OK.equals(result)) {
            SysUser puser = sysUserService.selectByKey(user.getId());
            if (puser == null) {
                return HttpServletResponseUtil.processModelAndView(map, "请重新登录修改密码", MODIFYPAGE);
            }
            String newPassword = new SimpleHash(Constants.ALGORITHMNAME, user.getPassword(),
                    ByteSource.Util.bytes(puser.getUsername()), Constants.HASHITERATIONS).toHex();
            if (!puser.getPassword().equals(newPassword)) {
                return HttpServletResponseUtil.processModelAndView(map, "原始密码不正确", MODIFYPAGE);
            }
            if (!user.getNpassword().equals(user.getCpassword())) {
                return HttpServletResponseUtil.processModelAndView(map, "两次填写新密码不匹配", MODIFYPAGE);
            }
            return processModifyPassword(user, puser, request);
        } else {
            return result;
        }
    }

    private String processModifyPwd(SysUser user, Model map) {
        if (user == null) {
            return HttpServletResponseUtil.processModelAndView(map, "请填写密码及确认密码", MODIFYPAGE);
        }
        if (StringUtils.isEmpty(user.getPassword())) {
            return HttpServletResponseUtil.processModelAndView(map, "请填写原始密码", MODIFYPAGE);
        }
        if (StringUtils.isEmpty(user.getNpassword())) {
            return HttpServletResponseUtil.processModelAndView(map, "请填写新密码", MODIFYPAGE);
        }
        if (StringUtils.isEmpty(user.getCpassword())) {
            return HttpServletResponseUtil.processModelAndView(map, "请填写确认密码", MODIFYPAGE);
        }
        return Constants.OK;
    }

    /**
     * 处理修改密码
     *
     * @param user    用户
     * @param puser   老用户
     * @param request 请求
     * @return 页面
     */
    private String processModifyPassword(SysUser user, SysUser puser, HttpServletRequest request) {
        puser.setPassword(user.getNpassword());
        PasswordHelper passwordHelper = new PasswordHelper();
        passwordHelper.encryptPassword(puser);
        sysUserService.updateAll(puser, request);
        return INDEX;
    }

    /**
     * 403页面
     *
     * @return 页面
     */
    @RequestMapping("/403")
    @SystemControllerLog(description = "打开无权限页面")
    public String forbidden() {
        return "error/403";
    }

    /**
     * 404页面
     *
     * @return 页面
     */
    @RequestMapping("/404")
    @SystemControllerLog(description = "打开错误页面")
    public String error() {
        return "error/404";
    }

    /**
     * 500页面
     *
     * @return 页面
     */
    @RequestMapping("/500")
    @SystemControllerLog(description = "打开内部错误页面")
    public String innerError() {
        return "error/500";
    }

}
